# coding=utf-8
"""
    配置文件读取
"""
import logging
import os
import threading
from typing import Dict, List

import yaml

from utils.path_util import CONF_FOLDER


# @Time    :  2024-10-17 09:38:19
# @Author  :  jiangtong
# @Email   :  gingerqgyy@outlook.com
# @Project :  ginger_yl_test_boost
# @File    :  conf_util


class ConfUtil:
    """ GCP 配置文件读取类 """

    logger = None
    _instance = None
    _lock = threading.Lock()

    def __repr__(self):
        return f'全局 DoConf 对象'

    # 单例 : ( 非多线程单例 )
    def __new__(cls, *args, **kwargs):
        # 实例 :
        with cls._lock:
            if cls._instance is None:
                cls.logger = logging.getLogger(__name__)
                cls.logger.info('DoConf 实例创建')
                cls._instance = super().__new__(cls, *args, **kwargs)
                cls._instance.__init_log()
        return cls._instance

    def __init_log(self):
        if not self.logger.handlers:
            # 如果为设置 handler, 表示当前处于本地调试, 需要手动设置 handler, 并更新 logger 等级为 DEBUG :
            self.logger.setLevel(logging.DEBUG)
            console_handler = logging.StreamHandler()
            console_handler.setLevel(logging.DEBUG)
            self.logger.addHandler(console_handler)

    # 递归取值 :
    def __get_dict_data(self, dict_data: dict, *keys, first_call=True):
        """
        利用递归, 根据 *keys 依次从 di_data 中取值

        因为是递归取值, 故不在该方法中打印日志, 需要调用方打印 !

        :param dict_data: 字典
        :param keys: 不定长参数, 层级字典每层的 key 组成该不定长参数
        :param first_call: 是否首次调用, 首次调用时, 日志中会打印 keys
        :return:
            1. keys 没有传递, 直接返回 di_data
            2. di_data 未传递, 直接返回 None
            3. 中间某层未取到数据, 则返回 None
            4. 取到了数据, 则返回获取到的数据
            5. keys 某层取到非 dict 数据, 直接返回该数据
        """
        # 首次调用, 打印层级 keys :
        if first_call:
            # self.logger.debug(f'层级取值开始, keys 有: << {keys if keys else '未传 keys 取全量'} >>')
            pass

        # 当出现非 Dict 且非 List 类型的值, 返回该值 :
        if (not isinstance(dict_data, Dict) and not isinstance(dict_data, List)) and keys:
            # self.logger.debug(f'<< {dict_data} >> 已不是 Dict 类型, 故返回该值')
            return dict_data

        # 进行层级取值 :
        if dict_data and keys:
            key = keys[0]
            dict_data = dict_data[key]
            # self.logger.debug(f'{"根据" if first_call else "再根据"} << {key} >> 获取到 << {dict_data} >>')
            return self.__get_dict_data(dict_data, *keys[1:], first_call=False)

        # 当 dict_data / keys 中任意一个不存在时, 返回 dict_data
        # self.logger.debug(f'层级取值结束, 取到结果: << {dict_data} >>')
        return dict_data

    # 取值 :
    def get(self, file_name, *keys, conf_folder=CONF_FOLDER):
        """
        从 file_name 中根据 keys 取值
        :param file_name: 目标配置文件 :
        :param keys: 不定长参数, 层级 key
        :param conf_folder: 配置文件路径, 默认是 {CONF_FOLDER}
        :return:
        """
        _file = os.sep.join([conf_folder, f'{file_name}.yml'])
        with open(_file, encoding='utf8') as _f:
            _datas: dict = yaml.safe_load(_f)
        value = self.__get_dict_data(_datas, *keys)
        self.logger.info(f'从配置文件 << {file_name} >> 中根据 << {keys} >> 获取到数据 << {value} >>')
        return value

    # 存值 :
    def set(self, key, value, file_name, conf_folder=CONF_FOLDER):
        """
        向配置文件中存值 :
        :param key: 写入/更新的键
        :param value: 写入/更新的值
        :param file_name: 文件名
        :param conf_folder: 文件路径
        :return:
        """
        with self._lock:
            # 文件 :
            _file = os.sep.join([conf_folder, f'{file_name}.yml'])

            # 取值并更新 :
            with open(_file, encoding='utf8') as _f:
                _datas: dict = yaml.safe_load(_f)
                _datas = _datas if _datas else {}
            _datas[key] = value

            # 存回文件 :
            with open(_file, 'w', encoding='utf8') as _f:
                yaml.safe_dump(_datas, _f, default_flow_style=False, allow_unicode=True, encoding='utf8', sort_keys=True)

            self.logger.info(f'向文件 << {file_name} >> 更新或写入 << {{ {key} : {value} }} >> 成功')


conf_handler = ConfUtil()

if __name__ == '__main__':
    conf_handler.get('users', 0, 'account')
    conf_handler.get('users', 1, 'account')
    conf_handler.get('users', 2, 'account')
    conf_handler.get('users', 3, 'account')
